package edu.ptu.shortlink.gateway.filter;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import edu.ptu.shortlink.gateway.config.Config;
import edu.ptu.shortlink.gateway.dto.GatewayErrorResult;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import reactor.core.publisher.Mono;

import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Objects;

/**
 * @BelongsProject: short-link
 * @BelongsPackage: edu.ptu.shortlink.gateway.filter
 * @Author: HYJ
 * @CreateTime: 2025-05-16  00:26
 * @Description: 令牌验证网关过滤器工厂，用于验证请求中的token有效性
 * @Version: 1.0
 */
@Component
public class TokenValidateGatewayFilterFactory extends AbstractGatewayFilterFactory<Config> {

    private final StringRedisTemplate redisTemplate;

    public TokenValidateGatewayFilterFactory(StringRedisTemplate redisTemplate) {
        super(Config.class);
        this.redisTemplate = redisTemplate;
    }

    /**
     * 创建并配置网关过滤器
     *
     * @param config 过滤器配置
     * @return 配置好的网关过滤器
     */
    @Override
    public GatewayFilter apply(Config config) {
        return (exchange, chain) -> {
            // 白名单校验
            ServerHttpRequest request = exchange.getRequest();
            String requestPath = request.getPath().toString();
            String requestMethod = request.getMethod().name();

            // 检查请求路径是否在白名单中
            if (!isPathInWhiteList(requestPath, requestMethod, config.getWhitePathList())) {
                String username = request.getHeaders().getFirst("username");
                String token = request.getHeaders().getFirst("token");

                Object userInfo;
                // 验证用户名和token是否有效
                if (StringUtils.hasText(username) && StringUtils.hasText(token) && (userInfo = redisTemplate.opsForHash().get("short-link:login:" + username, token)) != null) {
                    // 解析用户信息并添加到请求头
                    JSONObject userInfoJsonObject = JSON.parseObject(userInfo.toString());
                    ServerHttpRequest.Builder builder = exchange.getRequest().mutate().header("username", username).headers(
                            httpHeaders -> {
                                httpHeaders.set("userId", userInfoJsonObject.getString("id"));
                                httpHeaders.set("realName", URLEncoder.encode(userInfoJsonObject.getString("realName"), StandardCharsets.UTF_8));
                            });
                    return chain.filter(exchange.mutate().request(builder.build()).build());
                }
                // 验证失败，返回未授权响应
                ServerHttpResponse response = exchange.getResponse();
                response.setStatusCode(HttpStatus.UNAUTHORIZED);

                return response.writeWith(Mono.fromSupplier(() -> {
                    DataBufferFactory bufferFactory = response.bufferFactory();
                    GatewayErrorResult errorResult = GatewayErrorResult.builder()
                            .status(HttpStatus.UNAUTHORIZED.value())
                            .message("未登录或登录已过期")
                            .build();
                    return bufferFactory.wrap(JSON.toJSONString(errorResult).getBytes());
                }));
            }
            return chain.filter(exchange);
        };
    }

    private boolean isPathInWhiteList(String requestPath, String requestMethod, List<String> whitePathList) {
        return (!CollectionUtils.isEmpty(whitePathList) && whitePathList.stream().anyMatch(requestPath::startsWith)) || (Objects.equals(requestPath, "/api/short-link/admin/v1/user") && Objects.equals(requestMethod, "POST"));
    }
}
